from typing import List

from utils.mongo_utils import get_library_info_collection, get_version_diff_info_collection
from config.log_config import get_logger

logger = get_logger()


def get_version_path(library: str, from_version: str, to_version: str) -> List[str]:
    """
    获取覆盖from_version和to_version的正向版本路径（从小到大）。
    """
    library_info_collection = get_library_info_collection()
    results = library_info_collection.find({"library": library})
    if not results:
        return []

    version_docs = results[0]
    version_items = version_docs["versionInfo"]
    version_list = [item["version"] for item in version_items]

    try:
        from_idx = version_list.index(from_version)
        to_idx = version_list.index(to_version)
    except ValueError:
        logger.error(f"版本 {from_version} 或 {to_version} 不在版本列表中")
        return []

    # 总是返回从小到大的路径
    start_idx = min(from_idx, to_idx)
    end_idx = max(from_idx, to_idx)

    return version_list[start_idx:end_idx + 1]


def merge_diff_functions(diff_list: List[dict]) -> List[dict]:
    """
    合并多个diff函数列表，处理函数的添加、删除、更新操作
    """
    function_states = {}  # 记录每个函数的状态

    for diff_item in diff_list:
        func_key = diff_item.get('function_key', diff_item.get('from_function_key', ''))
        diff_type = diff_item['type']

        if func_key not in function_states:
            function_states[func_key] = {
                'status': 'none',  # none, added, deleted, updated
                'last_operation': None,
                'operations': []
            }

        state = function_states[func_key]
        state['operations'].append(diff_type)

        if diff_type == 'add':
            if state['status'] == 'none':
                state['status'] = 'added'
                state['last_operation'] = 'add'
            elif state['status'] == 'deleted':
                # 先删除后添加，转换为更新
                state['status'] = 'updated'
                state['last_operation'] = 'update'
            elif state['status'] == 'added':
                # 重复添加，保持添加状态
                state['last_operation'] = 'add'
            elif state['status'] == 'updated':
                # 更新后又添加，保持更新状态
                state['last_operation'] = 'update'

        elif diff_type == 'delete':
            if state['status'] == 'none':
                state['status'] = 'deleted'
                state['last_operation'] = 'delete'
            elif state['status'] == 'added':
                # 先添加后删除，函数不存在
                state['status'] = 'none'
                state['last_operation'] = None
            elif state['status'] == 'deleted':
                # 重复删除，保持删除状态
                state['last_operation'] = 'delete'
            elif state['status'] == 'updated':
                # 更新后删除，转为删除
                state['status'] = 'deleted'
                state['last_operation'] = 'delete'

        elif diff_type == 'update':
            if state['status'] == 'none':
                # 没有函数却被更新，转为添加
                state['status'] = 'added'
                state['last_operation'] = 'add'
            elif state['status'] == 'added':
                # 添加后更新，保持添加状态
                state['last_operation'] = 'add'
            elif state['status'] == 'deleted':
                # 删除后更新，转为更新
                state['status'] = 'updated'
                state['last_operation'] = 'update'
            elif state['status'] == 'updated':
                # 重复更新，保持更新状态
                state['last_operation'] = 'update'

    # 生成最终的diff列表
    final_diffs = []
    for func_key, state in function_states.items():
        if state['status'] == 'added':
            final_diffs.append({
                'type': 'add',
                'function_key': func_key
            })
        elif state['status'] == 'deleted':
            final_diffs.append({
                'type': 'delete',
                'function_key': func_key
            })
        elif state['status'] == 'updated':
            # 对于更新，需要找到原始函数键
            original_key = None
            for diff_item in reversed(diff_list):
                if diff_item.get('from_function_key') == func_key or diff_item.get('function_key') == func_key:
                    if diff_item['type'] == 'update':
                        original_key = diff_item.get('from_function_key', func_key)
                        break
            final_diffs.append({
                'type': 'update',
                'from_function_key': original_key or func_key,
                'function_key': func_key
            })

    return final_diffs


def invert_diff_list(diff_list: List[dict]) -> List[dict]:
    """
    反转一个diff列表，包括操作类型和列表顺序。
    """
    if not diff_list:
        return []

    # 1. 先反转列表的顺序，因为 v3->v2->v1 的diff是 v1->v2->v3 diff的逆序
    reversed_list = diff_list[::-1]

    # 2. 再反转每个diff操作
    inverted_diffs = []
    for diff in reversed_list:
        inverted_diff = {}
        if diff['type'] == 'add':
            inverted_diff['type'] = 'delete'
            inverted_diff['function_key'] = diff.get('function_key')
        elif diff['type'] == 'delete':
            inverted_diff['type'] = 'add'
            inverted_diff['function_key'] = diff.get('from_function_key')
        elif diff['type'] == 'update':
            inverted_diff['type'] = 'update'
            # 反转更新操作时，from和to函数键需要互换
            inverted_diff['from_function_key'] = diff.get('function_key')
            inverted_diff['function_key'] = diff.get('from_function_key')

        if 'type' in inverted_diff:
            inverted_diffs.append(inverted_diff)

    return inverted_diffs


def extract_cross_version_diff(
        library: str,
        from_version: str,
        to_version: str
) -> List[dict]:
    """
    提取两个版本之间的跨版本diff函数，数据库只存正向diff。
    """
    # 1. 获取覆盖两个版本的正向路径（从小到大）
    forward_path = get_version_path(library, from_version, to_version)
    if not forward_path or len(forward_path) < 2:
        logger.warning(f"无法为 {from_version} -> {to_version} 生成有效的正向路径")
        return []

    # 2. 沿着正向路径收集所有正向diff
    all_forward_diffs = []
    version_diff_collection = get_version_diff_info_collection()
    for i in range(len(forward_path) - 1):
        v1 = forward_path[i]
        v2 = forward_path[i + 1]

        diff_doc = version_diff_collection.find_one({
            "library": library,
            "from_version": v1,
            "version": v2
        })

        if diff_doc and diff_doc.get("diff_function"):
            all_forward_diffs.extend(diff_doc.get("diff_function"))
        else:
            logger.warning(f"路径中缺失正向diff文档: {v1} -> {v2}")

    if not all_forward_diffs:
        logger.warning(f"在路径 {forward_path[0]} -> {forward_path[-1]} 上没有收集到任何diff函数")
        return []

    # 3. 判断原始请求方向，决定是否需要反转diff
    try:
        # 重新获取版本列表以确定索引
        library_info_collection = get_library_info_collection()
        version_list = [item["version"] for item in
                        library_info_collection.find_one({"library": library})["versionInfo"]]
        from_idx = version_list.index(from_version)
        to_idx = version_list.index(to_version)
    except (ValueError, TypeError):
        logger.error("无法确定版本顺序，将使用正向diff")
        from_idx, to_idx = 0, 1  # 默认正向

    if from_idx > to_idx:
        # 原始请求是反向的，需要反转整个diff列表
        final_diffs = invert_diff_list(all_forward_diffs)
    else:
        # 原始请求是正向的，直接使用
        final_diffs = all_forward_diffs

    # 4. 合并所有diff函数
    merged_final_diffs = merge_diff_functions(final_diffs)
    return merged_final_diffs


